import time
import requests
import json
import os

from coin.proto.coin_query_pb2 import CurrencyBalance
from coin.proto.coin_key_pb2 import CoinKeyProto
from coin.exchange.base.kr_rest.auth_base import clean_json


RETRY_INTERVAL_SEC = [1, 2, 4, 8]

def retry_error(query_fn, **kwargs):
  for delay in RETRY_INTERVAL_SEC:
    try:
      return query_fn(**kwargs)
    except requests.exceptions.HTTPError as e:
      print(type(e), e)
    except requests.exceptions.RequestException as e:
      print(f'Node problems. Trying again in {delay} seconds.')
    except ValueError as e:
      print(type(e), e)
    finally:
      time.sleep(delay)
  return query_fn(**kwargs)


def parse_coin_key(key_file):
  assert os.path.exists(key_file), key_file
  key = None
  with open(key_file) as f:
    json_str = clean_json(f.read())
    if json_str != "":
      key = json.loads(json_str)
  assert 'chain_keys' in key
  key = CoinKeyProto(**key)
  return key


def coin_unit_converter(unit):
  unit = unit.lower()
  if unit == 'wei':
    return 10 ** -18
  elif unit == 'satoshi':
    return 10 ** -8
  elif unit == 'sun':
    return 10 ** -6
  elif 'micro' in unit:
    return 10 ** -6
  elif unit == 'norm':
    return 1
  else:
    raise ValueError(unit)


def find_exact_target_block(
    target_timestamp,
    latest_target_block_number,
    privc,
    block_margin=10):
  curr_num = latest_target_block_number
  for _ in range(block_margin):
    next_num = curr_num - 1
    next_ts = privc.get_block_timestamp(next_num)
    if next_ts <= target_timestamp:
      return next_num
    else:
      curr_num = next_num
  return None


def calculate_block_height(privc, target_timestamp):
    latest_block_number, latest_block_timestamp = privc.get_latest_block_number_timestamp()
    if latest_block_timestamp <= target_timestamp:
      return latest_block_number
    general_target_block_number, _ = find_exact_target_block(
        target_timestamp, latest_block_number, privc)
    block_number = find_exact_target_block(
      target_timestamp,
      general_target_block_number,
      privc)
    return block_number


def parse_currency_balance(currency, unit, balance):
  assert currency is not None
  assert unit is not None
  assert balance is not None
  currency_balance = CurrencyBalance()
  currency_balance.currency = currency
  currency_balance.total = float(balance) * coin_unit_converter(unit)
  return currency_balance
